#!/usr/bin/env node
/* eslint-disable node/shebang */

'use strict';

/*
 * This script generates the template a changelog by comparing a current version
 * with the latest commit in a branch. Run this, copy what's logged into the
 * `CHANGELOG.md` and update the top section based on the changes listed in
 * "Community Contributions"
 *
 * Usage:
 *
 * dev/changelog <remote branch-name (Default: master)> <next version>
 */

const { Octokit } = require('@octokit/rest');
const semver = require('semver');
const gitRepoInfo = require('git-repo-info');

let currentVersion = `v${require('../package').version}`;
let branch = process.argv[2] || gitRepoInfo().branch;
let nextVersion = process.argv[3];
if (nextVersion === undefined) {
  if (branch === 'master') {
    // this is likely the "first beta" of a new cycle
    let nextMinor = semver.inc(currentVersion, 'minor');
    nextVersion = `${nextMinor}-beta.1`;
  } else if (branch === 'beta') {
    nextVersion = semver.inc(currentVersion, 'prerelease', 'beta');
  } else if (branch === 'release') {
    nextVersion = semver.inc(currentVersion, 'patch');
  } else {
    console.error('Cannot detect branch, please specify on the command line');
    process.exitCode = 1;
    return;
  }
}
if (nextVersion[0] !== 'v') {
  nextVersion = `v${nextVersion}`;
}

function generateChangelog(contributions) {
  return `
## ${nextVersion}

#### Blueprint Changes

- [\`ember new\` diff](https://github.com/ember-cli/ember-new-output/compare/${currentVersion}...${nextVersion})
- [\`ember addon\` diff](https://github.com/ember-cli/ember-addon-output/compare/${currentVersion}...${nextVersion})

#### Changelog

${contributions}

Thank you to all who took the time to contribute!
`;
}

// only filters when dependabot itself merges a PR
function excludeDependabot(commitInfo) {
  let author;

  if (commitInfo.author) {
    author = commitInfo.author.login;
  } else if (commitInfo.committer) {
    author = commitInfo.committer.login;
  } else {
    return true;
  }

  return author !== 'dependabot-preview[bot]' && author !== 'dependabot[bot]';
}

function isPullRequestMergeOrCherryPick(commitInfo) {
  let isMergeCommit = commitInfo.parents.length > 1;

  let message = commitInfo.commit.message;

  let isReleaseMergeUpwardsCommit = isMergeCommit && message.includes("Merge remote-tracking branch 'origin/release'");
  let isBetaMergeUpwardsCommit = isMergeCommit && message.includes("Merge remote-tracking branch 'origin/beta'");

  let isCherryPick = message.includes('cherry picked from');

  return !isReleaseMergeUpwardsCommit && !isBetaMergeUpwardsCommit && (isCherryPick || isMergeCommit);
}

function comparePrNumber(a, b) {
  if (a.number < b.number) {
    return -1;
  }
  if (a.number > b.number) {
    return 1;
  }
  return 0;
}

async function processPages(res) {
  return res.data.commits
    .filter(excludeDependabot)
    .filter(isPullRequestMergeOrCherryPick)
    .map(commitInfo => {
      let message = commitInfo.commit.message;

      let mergeFromBranchRegex = /#(\d+) from (.*)\//;
      let mergeWithPrReferenceRegex = /\(#(\d+)\)$/m;

      let result = {
        commitInfo,
        author: undefined,
        number: undefined,
        title: undefined,
      };

      if (mergeFromBranchRegex.test(message)) {
        let match = message.match(mergeFromBranchRegex);
        let numAndAuthor = match.slice(1, 3);

        result.number = numAndAuthor[0];
      } else if (mergeWithPrReferenceRegex.test(message)) {
        let match = message.match(mergeWithPrReferenceRegex);
        result.number = match[1];
        result.title = message.split('\n')[0];
      }

      return result;
    })
    .sort(comparePrNumber);
}

async function main() {
  let github = new Octokit({ version: '3.0.0', auth: process.env.GITHUB_AUTH });

  github.repos
    .compareCommits({
      owner: 'ember-cli',
      repo: 'ember-cli',
      base: currentVersion,
      head: branch,
    })
    .then(processPages)
    .then(async contributions => {
      for (let entry of contributions) {
        if (entry.number) {
          let prInfo = await github.pulls.get({
            owner: 'ember-cli',
            repo: 'ember-cli',
            // eslint-disable-next-line
          pull_number: entry.number
          });

          entry.title = prInfo.data.title;
          entry.author = prInfo.data.user.login;
        } else {
          // didn't find a PR
          entry.title = entry.commitInfo.commit.message.split('\n\n')[0];
          entry.author = entry.commitInfo.author.login;
        }
      }

      let changelogEntries = contributions
        // filters PR's _from_ dependabot that were merged manually
        .filter(entry => entry.author !== 'dependabot-preview[bot]' && entry.author !== 'dependabot[bot]')
        .map(pr => {
          let link;
          if (pr.number) {
            link = `[#${pr.number}](https://github.com/ember-cli/ember-cli/pull/${pr.number})`;
          } else {
            link = `[${pr.commitInfo.sha}](${pr.commitInfo.html_url})`;
          }
          let title = pr.title;
          let author = `[@${pr.author}]` + `(https://github.com/${pr.author})`;

          return `- ${link} ${title} ${author}`;
        })
        .join('\n');

      let changelog = generateChangelog(changelogEntries);

      console.log(changelog);
    })
    .catch(err => {
      console.error(err);
    });
}

main();
